package edu.towson.cosc.cosc455.jkingsbury.compiler;

import java.util.Stack;

/**
 * @author jkings3
 * Here is where the meaning is given
 */
public class MySemanticAnalyzer {
//Check for semantic errors, if none write to output file
	private Stack rightWay = new Stack();//So in correct order(#hai = #kthxbye)
	private Stack tags = new Stack();//All tags, hopefully they should be in matching order
	Stack holdVar = new Stack();
	Stack varName = new Stack();
	Stack varVal = new Stack();
	public String temp = "";
	public boolean passed = false;
	public boolean here = false;
	public boolean isVar = false;
	int does = 1;
	public int list = 0;
	int loc = 0;
	boolean inner = false;
	
	//Flip stack around and fill tag stack
	public void begin() {
		Stack temp = MyCompiler.Lexer.goodWord;//Get stack of elements
		while(!temp.isEmpty()){
			//Put it the right way
			rightWay.push(temp.peek());
			temp.pop();
		}
		//printStack(rightWay);
		createString();
	}
	
	private void createString() {
		addHai();//Take care of #hai
		temp = getNext();
		if(temp.equalsIgnoreCase(LexicalTokens.COMM_BEGIN)){
			addComm();
			passed = true;
			temp = getNext();
		}	
		if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_VAR)){
			addVar();
			passed = true;
			temp = getNext();
			while(temp.equalsIgnoreCase(LexicalTokens.BEGIN_VAR)){
				addVar();
				temp = getNext();
			}
			
		}	
		if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_HEAD)){
			addHead();
			passed = true;
			temp = getNext();
		}
		body();
		if(temp.equalsIgnoreCase(LexicalTokens.DOC_END)){
			MyCompiler.result += "</html>";
			//End of document
		}
		System.out.println(MyCompiler.result);
	}

	//Body
	private void body() {
		while(!temp.equalsIgnoreCase(LexicalTokens.DOC_END)){
			if(temp.equalsIgnoreCase(LexicalTokens.COMM_BEGIN)){
				addComm();
			}
			if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_BOLD)){
				addBold();
			}
			if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_ITALIC)){
				addItalics();
			}
			if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_LIST)){
				addList();
			}
			if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_PARA)){
				addPara();
			}
			innerText();
			if(!temp.equalsIgnoreCase(LexicalTokens.DOC_END))
				temp = getNext();//Update
		}
	}

	private void innerText() {
		while(!temp.equalsIgnoreCase(LexicalTokens.END_TAG_2) && !temp.equalsIgnoreCase(LexicalTokens.DOC_END) && !temp.equalsIgnoreCase(LexicalTokens.END_TAG)){
			if(temp.equalsIgnoreCase(LexicalTokens.VISIBLE)){
				use();
				here = true;
			}
			if(temp.equalsIgnoreCase(LexicalTokens.BREAK)){
				addNewline();
				here = true;
			}
			if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_AUDIO)){
				addSound();
				here = true;
			}
			if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_VIDEO)){
				addVideo();
				here = true;
			}
			text();
			if(here)
				temp = getNext();//Update
		}
	}

	private void text() {
		if(!tag(temp)){
			here = true;
			MyCompiler.result += temp;//Can only have words inside comment so don't need to do any more checking
		}
	}

	private void addHead() {
		MyCompiler.result += "<head>";//Add opening tag
		temp = getNext();
		addTitle();//Must have a title next so must go here next
		MyCompiler.result += "</head>";
	}

	private void addTitle() {
		MyCompiler.result += "<title> ";//Add opening tag
		temp = getNext();
		//Checking for words inside
		if(temp.charAt(0) != '#'){
			MyCompiler.result += temp;//Can only have words inside comment so don't need to do any more checking
			temp = getNext();
		}
		MyCompiler.result += "</title>";
	}
	
	private void addComm() {
		MyCompiler.result += "<!-- ";
		temp = getNext();
		//Checking for words inside
		if(temp.charAt(0) != '#'){
			MyCompiler.result += temp;//Can only have words inside comment so don't need to do any more checking
			temp = getNext();
		}
		MyCompiler.result += "-->";
	}

	private void addVar() {
		boolean var = false;
		if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_VAR)){//#I HAS A
			var = true;
			isVar = true;
			temp = getNext();//Skip over the tag
			varName.push(temp);
		}
		if(var)
			temp = getNext();//Get the next tag, #ITZ
		if(temp.equalsIgnoreCase(LexicalTokens.VARNAME)){
			temp = getNext();//Skip over the tag
			varVal.push(temp);
		}
		if(var){
			temp = getNext();//Get the next tag
		}
	}
	
	private void use(){
		boolean there;
		boolean more = false;
		String vari = (String) varVal.peek();
		if(temp.equalsIgnoreCase(LexicalTokens.VISIBLE)){
			temp = getNext();//Skip the #VISIBLE tag
			//Check to see if the var they want to use is there
			//If there are no current var names being held report error
			if(varName.isEmpty()){
				System.err.println("STATIC SEMANTIC ERROR. Trying to use an undefined variable.");
				System.exit(0);
			}
			if(!temp.equalsIgnoreCase((String) varName.peek())){
				if(varName.size() > 1){
					more = true;
					//Find if it exist in the stack
					there = traverseStack(varName);
					//Find location of it in varVal
					vari = findVal(varVal);
					MyCompiler.result += vari;
					temp = getNext();
				}
				//Check if it is defined somewhere else in the stack and at what position
				//Does not equal current name being help
				else{
					System.err.println("STATIC SEMANTIC ERROR. Current defined variable is " + varName.peek() + "and you are trying to use " + temp + ".");
					System.exit(0);
				}
			}
			//Else good match
			else{
				MyCompiler.result += vari;
			}
			loc = -1;
		}
	}
	
	private void addPara() {
		MyCompiler.result += "<p>";//Add opening tag
		//Check for nothing in para
		temp = getNext();
		isVar = false;
		addVar();
		innerPara();
		MyCompiler.result += "</p>";
		if(isVar){
			varName.pop();
			varVal.pop();
		}
	}
	
	private void innerPara() {
		boolean passed = true;
		while(!temp.equalsIgnoreCase(LexicalTokens.END_TAG) && passed==true && !temp.equalsIgnoreCase(LexicalTokens.DOC_END)){
			if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_BOLD)){
				addBold();
				passed = true;
			}
			if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_ITALIC)){
				addItalics();
				passed = true;
			}
			if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_LIST)){
				addList();
				passed = true;
			}
			if(temp.equalsIgnoreCase(LexicalTokens.BREAK)){
				addNewline();
				passed = true;
			}
			if(temp.equalsIgnoreCase(LexicalTokens.VISIBLE)){
				use();
				temp = getNext();
				here = true;
			}
			if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_AUDIO)){
				addSound();
				here = true;
			}
			if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_VIDEO)){
				addVideo();
				here = true;
			}
			text();
			if (passed = true){
				temp = getNext();//Update
			}
			else{
				passed = false;
			}
		}
	}

	private void addBold() {
		MyCompiler.result += "<b>";//Add opening tag
		temp = getNext();
		isVar = false;
		addVar();
		innerText();
		MyCompiler.result += "</b>";
		//If there is a var, pop it at the end of a block
		if(isVar){
			varName.pop();
			varVal.pop();
		}
	}
	private void addItalics() {
		MyCompiler.result += "<i>";//Add opening tag
		temp = getNext();
		isVar = false;
		addVar();
		innerText();
		MyCompiler.result += "</i>";
		if(isVar){
			varName.pop();
			varVal.pop();
		}
	}
	
	private void addList() {
		MyCompiler.result += "<ul>";//Add opening tag
		temp = getNext();
		addListItem();//Not optional
		innerList();//Check for list insides
		temp = getNext();
		//Check for more items
		while(temp.equalsIgnoreCase(LexicalTokens.BEGIN_ITEM) || temp.equalsIgnoreCase(LexicalTokens.END_TAG_2)){
			addListItem();
			temp = getNext();//Update
		}
		MyCompiler.result += "</ul>";
	}
	private void addListItem() {
		if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_ITEM))
			MyCompiler.result += "<li>";//Add opening tag
		innerList();
		if(temp.equalsIgnoreCase(LexicalTokens.END_TAG_2))
			MyCompiler.result += "</li>";
	}
	private void innerList() {
		while(!temp.equalsIgnoreCase(LexicalTokens.END_TAG_2) && !temp.equalsIgnoreCase(LexicalTokens.END_TAG)){
			if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_BOLD)){
				addBold();
			}
			if(temp.equalsIgnoreCase(LexicalTokens.BEGIN_ITALIC)){
				addItalics();
			}
			if(MyCompiler.token.equalsIgnoreCase(LexicalTokens.BEGIN_LIST)){
				list++;
				addList();
			}
			innerText();
			//If nested list
			if(list > 1)
				temp = getNext();//Update
		}
	}

	private void addNewline() {
		MyCompiler.result += "<br>";//Add opening tag
	}
	private void addSound() {
		MyCompiler.result += "<audio controls>";//Add opening tag
		temp = getNext();
		//Checking for words inside
		if(temp.charAt(0) != '#'){
			MyCompiler.result += temp;//Can only have words inside comment so don't need to do any more checking
			temp = getNext();
		}
		else{
			System.err.println("SEMANTIC ERROR. Cannot have an audio tag without any audio.");
			System.exit(0);
		}
		MyCompiler.result += "</audio>";
	}
	private void addVideo() {
		MyCompiler.result += "<iframe src=\"";//Add opening tag
		temp = getNext();
		//Checking for words inside
		if(temp.charAt(0) != '#'){
			MyCompiler.result += temp;//Can only have words inside comment so don't need to do any more checking
			temp = getNext();
		}
		else{
			System.err.println("SEMANTIC ERROR. Cannout have a video tag without a video.");
			System.exit(0);
		}
		MyCompiler.result += "\"/>";//Add close of tag
	}

	private String getNext() {
		rightWay.pop();
		if(!rightWay.isEmpty() && !temp.equalsIgnoreCase(LexicalTokens.DOC_END))
			return (String) rightWay.peek();
		else
			return "";
	}

	private void addHai() {
		MyCompiler.result += "<html>";//Will be "#hai" at top
	}

	private boolean tag(String stuff) {
		return MyCompiler.Lexer.lookupToken(stuff);
	}

	public boolean traverseStack(Stack s) {
		Stack tempo = new Stack ();
		boolean toRet = false;
		while(!s.empty()){
			loc++;
		      if(temp.equalsIgnoreCase((String) s.peek()))
		    	  toRet = true;
		      tempo.push(s.peek());
		      s.pop();
		   }
		while(!tempo.empty()){
			s.push(tempo.peek());
			tempo.pop();
		}
		return toRet;
	}
	
	public String findVal(Stack s) {
		int num = 0;
		String toRet = "";
		Stack tempo = new Stack ();
		while(!s.empty()){
			num++;
			if(num == loc){
				toRet = (String) s.peek();
			}
			tempo.push(s.peek());
		    s.pop();
		}
		while(!tempo.empty()){
			s.push(tempo.peek());
			tempo.pop();
		}
		return toRet;
	}
	
	void printStack(Stack s) {
		//int count = 0;
		while(!s.empty()){
		      System.out.println(s.peek());
		      s.pop();
		   }
	}
	
}
